#include "ExMainHUD.h"
#include <QMessageBox>
#include <QDebug>
#include "qevent.h"
#include <experimental/resumable>
#include <filesystem>
#include "DDSTextureLoader.h"
#include "WICTextureLoader.h"

using namespace std::experimental;
using namespace DirectX;
using namespace std::filesystem;


#ifndef HR
#define HR(x)												\
	{															\
		HRESULT hr = (x);										\
		if(FAILED(hr))											\
		{														\
			QMessageBox::critical(this, "critical", QString("file[%1]line[%2]").arg(__FILEW__).arg(__LINE__));\
		}														\
	}
#endif

// 安全COM组件释放宏
#define SAFE_RELEASE(p) { if ((p)) { (p)->Release(); (p) = nullptr; } }

const D3D11_INPUT_ELEMENT_DESC ExMainHUD::VertexPosColorTex::inputLayout[3] = {
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

ExMainHUD::ExMainHUD(QWidget* parent)
	: QWidget(parent),
	m_AppPaused(false),
	m_Enable4xMsaa(true),
	m_4xMsaaQuality(0),
	m_pd3dDevice(nullptr),
	m_pd3dImmediateContext(nullptr),
	m_pSwapChain(nullptr),
	m_pDepthStencilBuffer(nullptr),
	m_pRenderTargetView(nullptr),
	m_pDepthStencilView(nullptr)
{
	ui.setupUi(this);

	//设置窗口属性，关键步骤，否则D3D绘制出问题
	ui.DirectXWidget->setAttribute(Qt::WA_PaintOnScreen, true);
	ui.DirectXWidget->setAttribute(Qt::WA_NativeWindow, true);

	m_ClientWidth = ui.DirectXWidget->width();
	m_ClientHeight = ui.DirectXWidget->height();

	//鼠标键盘初始化
	m_pMouse = std::make_unique<DirectX::Mouse>();
	m_pKeyboard = std::make_unique<DirectX::Keyboard>();

	// 初始化鼠标，键盘不需要
	m_pMouse->SetWindow((HWND)ui.DirectXWidget->winId());
	m_pMouse->SetMode(DirectX::Mouse::MODE_ABSOLUTE);

	//3D图形窗口初始化
	if (!InitDirect3D())
		return;

	if (!InitEffect())
		return;

	if (!InitResource())
		return;

	//重置游戏时间管理器
	m_Timer.Reset();


	//多线程模式开启渲染流, 保持帧率为60
	RenderThread = new ExRenderThread(this);
	connect(RenderThread, SIGNAL(RenderRun()), this, SLOT(UpdateRender()));
	RenderThread->start();

}

bool ExMainHUD::InitDirect3D()
{
	HRESULT hr = S_OK;

	//创建D3D设备和D3D设备上下文
	UINT createDeviceFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)  
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	// 驱动类型数组, 在下面的数组里循环, 看哪个可以用
	D3D_DRIVER_TYPE driverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	UINT numDriverTypes = ARRAYSIZE(driverTypes);

	// 特性等级数组, 先尝试创建11.1的, 如果不行再尝试创建11.0
	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
	};
	UINT numFeatureLevels = ARRAYSIZE(featureLevels);

	//创建设备和上下文方法输出的特性等级
	D3D_FEATURE_LEVEL featureLevel;
	D3D_DRIVER_TYPE d3dDriverType;
	for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
	{
		d3dDriverType = driverTypes[driverTypeIndex];
		hr = D3D11CreateDevice(nullptr, d3dDriverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
			D3D11_SDK_VERSION, m_pd3dDevice.GetAddressOf(), &featureLevel, m_pd3dImmediateContext.GetAddressOf());

		if (hr == E_INVALIDARG)
		{
			// Direct3D 11.0 的API不承认D3D_FEATURE_LEVEL_11_1，所以我们需要尝试特性等级11.0以及以下的版本
			hr = D3D11CreateDevice(nullptr, d3dDriverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
				D3D11_SDK_VERSION, m_pd3dDevice.GetAddressOf(), &featureLevel, m_pd3dImmediateContext.GetAddressOf());
		}

		if (SUCCEEDED(hr))
			break;
	}

	if (FAILED(hr))
	{
		QMessageBox::warning(this, "warning", "D3D11CreateDevice Failed.");
		return false;
	}

	// 检测是否支持特性等级11.0或11.1
	if (featureLevel != D3D_FEATURE_LEVEL_11_0 && featureLevel != D3D_FEATURE_LEVEL_11_1)
	{
		QMessageBox::warning(this, "warning", "Direct3D Feature Level 11 unsupported.");
		return false;
	}

	// 检测 MSAA支持的质量等级
	m_pd3dDevice->CheckMultisampleQualityLevels(
		DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m_4xMsaaQuality);
	assert(m_4xMsaaQuality > 0);

	//dxgi缓存区相关类, 用于实现DXGI交换链
	ComPtr<IDXGIDevice> dxgiDevice = nullptr;
	ComPtr<IDXGIAdapter> dxgiAdapter = nullptr;
	ComPtr<IDXGIFactory1> dxgiFactory1 = nullptr;	// D3D11.0(包含DXGI1.1)的接口类
	ComPtr<IDXGIFactory2> dxgiFactory2 = nullptr;	// D3D11.1(包含DXGI1.2)特有的接口类

	// 为了正确创建 DXGI交换链，首先我们需要获取创建 D3D设备 的 DXGI工厂，否则会引发报错：
	// "IDXGIFactory::CreateSwapChain: This function is being called with a device from a different IDXGIFactory."
	HR(m_pd3dDevice.As(&dxgiDevice));
	HR(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf()));
	HR(dxgiAdapter->GetParent(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(dxgiFactory1.GetAddressOf())));

	// 查看该对象是否包含IDXGIFactory2接口
	hr = dxgiFactory1.As(&dxgiFactory2);
	// 如果包含，则说明支持D3D11.1
	if (dxgiFactory2 != nullptr)
	{
		//在上面使用dxgiFactory1不为空判断到支持DX11.1后, 才把设备和上下文转换成DX11.1
		HR(m_pd3dDevice.As(&m_pd3dDevice1));
		HR(m_pd3dImmediateContext.As(&m_pd3dImmediateContext1));
		//填充各种结构体用以描述交换链
		DXGI_SWAP_CHAIN_DESC1 sd;
		//用0来填充这块区域
		ZeroMemory(&sd, sizeof(sd));
		sd.Width = m_ClientWidth;
		sd.Height = m_ClientHeight;
		sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		// 是否开启4倍多重采样？
		if (m_Enable4xMsaa)
		{
			sd.SampleDesc.Count = 4;
			sd.SampleDesc.Quality = m_4xMsaaQuality - 1;
		}
		else
		{
			sd.SampleDesc.Count = 1;
			sd.SampleDesc.Quality = 0;
		}
		sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		sd.BufferCount = 1;
		sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		sd.Flags = 0;

		//更新配置信息
		DXGI_SWAP_CHAIN_FULLSCREEN_DESC fd;
		fd.RefreshRate.Numerator = 60;
		fd.RefreshRate.Denominator = 1;
		fd.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		fd.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		fd.Windowed = TRUE;
		// 为当前窗口创建交换链
		HR(dxgiFactory2->CreateSwapChainForHwnd(m_pd3dDevice.Get(), (HWND)ui.DirectXWidget->winId(), &sd, &fd, nullptr, m_pSwapChain1.GetAddressOf()));
		HR(m_pSwapChain1.As(&m_pSwapChain));
	}
	else
	{
		// 填充DXGI_SWAP_CHAIN_DESC用以描述交换链
		DXGI_SWAP_CHAIN_DESC sd;
		ZeroMemory(&sd, sizeof(sd));
		sd.BufferDesc.Width = m_ClientWidth;
		sd.BufferDesc.Height = m_ClientHeight;
		sd.BufferDesc.RefreshRate.Numerator = 60;
		sd.BufferDesc.RefreshRate.Denominator = 1;
		sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		// 是否开启4倍多重采样？
		if (m_Enable4xMsaa)
		{
			sd.SampleDesc.Count = 4;
			sd.SampleDesc.Quality = m_4xMsaaQuality - 1;
		}
		else
		{
			sd.SampleDesc.Count = 1;
			sd.SampleDesc.Quality = 0;
		}
		sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		sd.BufferCount = 1;
		sd.OutputWindow = (HWND)ui.DirectXWidget->winId();
		sd.Windowed = TRUE;
		sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		sd.Flags = 0;
		HR(dxgiFactory1->CreateSwapChain(m_pd3dDevice.Get(), &sd, m_pSwapChain.GetAddressOf()));
	}

	// 可以禁止alt+enter全屏
	dxgiFactory1->MakeWindowAssociation((HWND)ui.DirectXWidget->winId(), DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES);

	// 每当窗口被重新调整大小的时候，都需要调用这个OnResize函数。现在调用
	// 以避免代码重复
	OnResize();
}


bool ExMainHUD::InitEffect()
{
	DWORD ShaderFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)  
	ShaderFlags |= D3D10_SHADER_DEBUG;
	ShaderFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
#endif

	// ComPtr<ID3DBlob> compiledShader;
	ComPtr<ID3DBlob> compilationMsgs = 0;
	HR(D3DX11CompileEffectFromFile(L"HLSL\\Cube.fx", nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, ShaderFlags, 0, m_pd3dDevice.Get(), m_pFX.GetAddressOf(), compilationMsgs.GetAddressOf()));

	if (compilationMsgs.Get() != 0)
	{
		qDebug() << "compilationMsgs Exit ! " << compilationMsgs.Get();
		SAFE_RELEASE(compilationMsgs);
		// return false;
	}

#if 0
	ComPtr<ID3DBlob> compilationTempMsgs = 0;
	ComPtr<ID3DX11Effect> m_pTempFX = 0;
	HR(D3DX11CompileEffectFromFile(L"HLSL\\Color.fx", nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, ShaderFlags, 0, m_pd3dDevice.Get(), m_pTempFX.GetAddressOf(), compilationTempMsgs.GetAddressOf()));

	if (compilationTempMsgs.Get() != 0)
	{
		qDebug() << "compilationTempMsgs Exit ! " << compilationTempMsgs.Get();
		SAFE_RELEASE(compilationTempMsgs);
		// return false;
	}
#endif

#if 0
	HR(D3DX11CreateEffectFromMemory(compiledShader->GetBufferPointer(), compiledShader->GetBufferSize(), 0, m_pd3dDevice.Get(), m_pFX.GetAddressOf()));

	SAFE_RELEASE(compiledShader);
#endif

	m_pTech = m_pFX->GetTechniqueByName("ColorTech");

	m_pWorld = m_pFX->GetVariableByName("g_World")->AsMatrix();

	m_pView = m_pFX->GetVariableByName("g_View")->AsMatrix();

	m_pProj = m_pFX->GetVariableByName("g_Proj")->AsMatrix();

	m_pTex = m_pFX->GetVariableByName("g_Tex")->AsShaderResource();

	return true;
}

bool ExMainHUD::InitResource()
{
	VertexPosColorTex vertices[] =
	{
		// 右面(+X面)
		{ XMFLOAT3(1.f, -1.f, -1.f), XMFLOAT4(0.f, 0.f, 0.f, 1.f), XMFLOAT2(0.f, 1.f) },
		{ XMFLOAT3(1.f, 1.f, -1.f), XMFLOAT4(1.f, 0.f, 0.f, 1.f), XMFLOAT2(0.f, 0.f) },
		{ XMFLOAT3(1.f, 1.f, 1.f), XMFLOAT4(1.f, 1.f, 0.f, 1.f), XMFLOAT2(1.f, 0.f) },
		{ XMFLOAT3(1.f, -1.f, 1.f), XMFLOAT4(0.f, 1.f, 0.f, 1.f), XMFLOAT2(1.f, 1.f) },
		// 左面(-X面)
		{ XMFLOAT3(-1.f, -1.f, 1.f), XMFLOAT4(0.f, 0.f, 1.f, 1.f), XMFLOAT2(0.f, 1.f) },
		{ XMFLOAT3(-1.f, 1.f, 1.f), XMFLOAT4(1.f, 0.f, 1.f, 1.f), XMFLOAT2(0.f, 0.f) },
		{ XMFLOAT3(-1.f, 1.f, -1.f), XMFLOAT4(1.f, 1.f, 1.f, 1.f), XMFLOAT2(1.f, 0.f) },
		{ XMFLOAT3(-1.f, -1.f, -1.f), XMFLOAT4(0.f, 1.f, 1.f, 1.f), XMFLOAT2(1.f, 1.f) },
		// 顶面(+Y面)
		{ XMFLOAT3(-1.f, 1.f, -1.f), XMFLOAT4(0.f, 0.f, 0.f, 1.f), XMFLOAT2(0.f, 1.f) },
		{ XMFLOAT3(-1.f, 1.f, 1.f), XMFLOAT4(1.f, 0.f, 0.f, 1.f), XMFLOAT2(0.f, 0.f) },
		{ XMFLOAT3(1.f, 1.f, 1.f), XMFLOAT4(1.f, 1.f, 0.f, 1.f), XMFLOAT2(1.f, 0.f) },
		{ XMFLOAT3(1.f, 1.f, -1.f), XMFLOAT4(0.f, 1.f, 0.f, 1.f), XMFLOAT2(1.f, 1.f) },
		// 底面(-Y面)
		{ XMFLOAT3(1.f, -1.f, -1.f), XMFLOAT4(0.f, 0.f, 1.f, 1.f), XMFLOAT2(0.f, 1.f) },
		{ XMFLOAT3(1.f, -1.f, 1.f), XMFLOAT4(1.f, 0.f, 1.f, 1.f), XMFLOAT2(0.f, 0.f) },
		{ XMFLOAT3(-1.f,-1.f, 1.f), XMFLOAT4(1.f, 1.f, 1.f, 1.f), XMFLOAT2(1.f, 0.f) },
		{ XMFLOAT3(-1.f,-1.f, -1.f), XMFLOAT4(0.f, 1.f, 1.f, 1.f), XMFLOAT2(1.f, 1.f) },
		// 背面(+Z面)
		{ XMFLOAT3(1.f, -1.f, 1.f), XMFLOAT4(0.f, 0.f, 0.f, 1.f), XMFLOAT2(0.f, 1.f) },
		{ XMFLOAT3(1.f, 1.f, 1.f), XMFLOAT4(1.f, 0.f, 0.f, 1.f), XMFLOAT2(0.f, 0.f) },
		{ XMFLOAT3(-1.f, 1.f, 1.f), XMFLOAT4(1.f, 1.f, 0.f, 1.f), XMFLOAT2(1.f, 0.f) },
		{ XMFLOAT3(-1.f, -1.f, 1.f), XMFLOAT4(0.f, 1.f, 0.f, 1.f), XMFLOAT2(1.f, 1.f) },
		// 正面(-Z面)
		{ XMFLOAT3(-1.f, -1.f, -1.f), XMFLOAT4(0.f, 0.f, 1.f, 1.f), XMFLOAT2(0.f, 1.f) },
		{ XMFLOAT3(-1.f, 1.f, -1.f), XMFLOAT4(1.f, 0.f, 1.f, 1.f), XMFLOAT2(0.f, 0.f) },
		{ XMFLOAT3(1.f, 1.f, -1.f), XMFLOAT4(1.f, 1.f, 1.f, 1.f), XMFLOAT2(1.f, 0.f) },
		{ XMFLOAT3(1.f, -1.f, -1.f), XMFLOAT4(0.f, 1.f, 1.f, 1.f), XMFLOAT2(1.f, 1.f) }
	};
	// 设置顶点缓冲区描述
	D3D11_BUFFER_DESC vbd;
	ZeroMemory(&vbd, sizeof(vbd));
	vbd.Usage = D3D11_USAGE_IMMUTABLE;
	vbd.ByteWidth = sizeof vertices;
	vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vbd.CPUAccessFlags = 0;
	// 新建顶点缓冲区
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory(&InitData, sizeof(InitData));
	InitData.pSysMem = vertices;
	HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer.GetAddressOf()));

	// ******************
	// 索引数组
	//
	WORD indices[] = {
		0, 1, 2, 2, 3, 0,		// 右面(+X面)
			4, 5, 6, 6, 7, 4,		// 左面(-X面)
			8, 9, 10, 10, 11, 8,	// 顶面(+Y面)
			12, 13, 14, 14, 15, 12,	// 底面(-Y面)
			16, 17, 18, 18, 19, 16, // 背面(+Z面)
			20, 21, 22, 22, 23, 20	// 正面(-Z面)
	};
	// 设置索引缓冲区描述
	D3D11_BUFFER_DESC ibd;
	ZeroMemory(&ibd, sizeof(ibd));
	ibd.Usage = D3D11_USAGE_IMMUTABLE;
	ibd.ByteWidth = sizeof indices;
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	ibd.CPUAccessFlags = 0;
	// 新建索引缓冲区
	InitData.pSysMem = indices;
	HR(m_pd3dDevice->CreateBuffer(&ibd, &InitData, m_pIndexBuffer.GetAddressOf()));
	// 输入装配阶段的索引缓冲区设置
	m_pd3dImmediateContext->IASetIndexBuffer(m_pIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);

	// ******************
	// 给渲染管线各个阶段绑定好所需资源
	//

	// 输入装配阶段的顶点缓冲区设置
	UINT stride = sizeof(VertexPosColorTex);	// 跨越字节数
	UINT offset = 0;						// 起始偏移量

	m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer.GetAddressOf(), &stride, &offset);
	// 设置图元类型，设定输入布局
	m_pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	D3DX11_PASS_DESC passDesc;
	m_pTech->GetPassByIndex(0)->GetDesc(&passDesc);
	HR(m_pd3dDevice->CreateInputLayout(VertexPosColorTex::inputLayout, ARRAYSIZE(VertexPosColorTex::inputLayout), passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, m_pVertexLayout.GetAddressOf()));

	m_pd3dImmediateContext->IASetInputLayout(m_pVertexLayout.Get());


	// ******************
	// 设置摄像机
	//
	m_pCamera = std::shared_ptr<ExFreeCamera>(new ExFreeCamera);
	m_pCamera->SetViewPort(0.f, 0.f, (float)m_ClientWidth, (float)m_ClientHeight);
	m_pCamera->LookAt(XMFLOAT3(), XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT3(0.0f, 1.0f, 0.0f));
	m_pCamera->SetFrustum(XM_PI / 3, AspectRatio(), 0.5f, 1000.0f);

	proj = m_pCamera->GetProjXM();

	// ******************
	// 设置图片, 像素作色阶段采样器
	//
	HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Texture\\WoodCrate.dds", nullptr, m_pWoodCrate.GetAddressOf()));
	m_pTex->SetResource(m_pWoodCrate.Get());

	return true;
}

void ExMainHUD::OnResize()
{
	assert(m_pd3dImmediateContext);
	assert(m_pd3dDevice);
	assert(m_pSwapChain);

	if (m_pd3dDevice1 != nullptr)
	{
		assert(m_pd3dImmediateContext1);
		assert(m_pd3dDevice1);
		assert(m_pSwapChain1);
	}

	// 释放渲染管线输出用到的相关资源
	m_pRenderTargetView.Reset();
	m_pDepthStencilView.Reset();
	m_pDepthStencilBuffer.Reset();

	// 重设交换链并且重新创建渲染目标视图, 显示到屏幕的数据是在交换链设备缓冲区中的, 通过GetBuffer可以获取, 获取之后使用设备的CreateRenderTargetView方法使用缓冲区数据生成目标视图
	ComPtr<ID3D11Texture2D> backBuffer;
	//根据窗口大小重置后备缓冲区的大小
	HR(m_pSwapChain->ResizeBuffers(1, m_ClientWidth, m_ClientHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
	//获取后备缓冲区
	HR(m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(backBuffer.GetAddressOf())));
	//绑定缓冲区给目标视图
	HR(m_pd3dDevice->CreateRenderTargetView(backBuffer.Get(), nullptr, m_pRenderTargetView.GetAddressOf()));

	backBuffer.Reset();

	D3D11_TEXTURE2D_DESC depthStencilDesc;

	depthStencilDesc.Width = m_ClientWidth;
	depthStencilDesc.Height = m_ClientHeight;
	depthStencilDesc.MipLevels = 1;
	depthStencilDesc.ArraySize = 1;
	depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;

	// 要使用 4X MSAA? --需要给交换链设置MASS参数
	if (m_Enable4xMsaa)
	{
		depthStencilDesc.SampleDesc.Count = 4;
		depthStencilDesc.SampleDesc.Quality = m_4xMsaaQuality - 1;
	}
	else
	{
		depthStencilDesc.SampleDesc.Count = 1;
		depthStencilDesc.SampleDesc.Quality = 0;
	}



	depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
	depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthStencilDesc.CPUAccessFlags = 0;
	depthStencilDesc.MiscFlags = 0;

	// 创建深度缓冲区以及深度模板视图, 先创建2D纹理
	HR(m_pd3dDevice->CreateTexture2D(&depthStencilDesc, nullptr, m_pDepthStencilBuffer.GetAddressOf()));
	//将创建好的2D纹理绑定到新建的深度/模板视图
	HR(m_pd3dDevice->CreateDepthStencilView(m_pDepthStencilBuffer.Get(), nullptr, m_pDepthStencilView.GetAddressOf()));


	// 将渲染目标视图和深度/模板缓冲区结合到管线
	m_pd3dImmediateContext->OMSetRenderTargets(1, m_pRenderTargetView.GetAddressOf(), m_pDepthStencilView.Get());

	// 设置视口变换
	m_ScreenViewport.TopLeftX = 0;
	m_ScreenViewport.TopLeftY = 0;
	m_ScreenViewport.Width = static_cast<float>(m_ClientWidth);
	m_ScreenViewport.Height = static_cast<float>(m_ClientHeight);
	m_ScreenViewport.MinDepth = 0.0f;
	m_ScreenViewport.MaxDepth = 1.0f;

	m_pd3dImmediateContext->RSSetViewports(1, &m_ScreenViewport);

	// 摄像机变更显示
	if (m_pCamera != nullptr)
	{
		m_pCamera->SetFrustum(XM_PI / 3, AspectRatio(), 0.5f, 1000.0f);
		m_pCamera->SetViewPort(0.0f, 0.0f, (float)m_ClientWidth, (float)m_ClientHeight);

		proj = m_pCamera->GetProjXM();
	}
}

void ExMainHUD::resizeEvent(QResizeEvent* event)
{
	QWidget::resizeEvent(event);

	m_ClientWidth = event->size().width() - 200;
	m_ClientHeight = event->size().height() - 200;

	ui.DirectXWidget->setGeometry(QRect(100, 100, m_ClientWidth, m_ClientHeight));

	OnResize();
}

void ExMainHUD::closeEvent(QCloseEvent* event)
{
	QWidget::closeEvent(event);
	RenderThread->stop();
	RenderThread->quit();
	RenderThread->wait();
}

float ExMainHUD::AspectRatio() const
{
	return static_cast<float>(m_ClientWidth) / m_ClientHeight;
}

void ExMainHUD::UpdateRender()
{

	m_Timer.Tick();

	CalculateFrameStats();
	UpdateScene(m_Timer.DeltaTime());
	DrawScene();
}

void ExMainHUD::UpdateScene(float dt)
{
	// 获取鼠标状态
	Mouse::State mouseState = m_pMouse->GetState();
	Mouse::State lastMouseState = m_MouseTracker.GetLastState();
	// 获取键盘状态
	Keyboard::State keyState = m_pKeyboard->GetState();
	Keyboard::State lastKeyState = m_KeyboardTracker.GetLastState();

	// 更新鼠标按钮状态跟踪器，仅当鼠标按住的情况下才进行移动
	m_MouseTracker.Update(mouseState);
	m_KeyboardTracker.Update(keyState);

	if (keyState.IsKeyDown(Keyboard::W))
		m_pCamera->MoveForward(dt * 6.f);
	if (keyState.IsKeyDown(Keyboard::S))
		m_pCamera->MoveForward(dt * -6.f);
	if (keyState.IsKeyDown(Keyboard::A))
		m_pCamera->Strafe(dt * -6.f);
	if (keyState.IsKeyDown(Keyboard::D))
		m_pCamera->Strafe(dt * 6.f);
	if (keyState.IsKeyDown(Keyboard::Q))
		m_pCamera->MoveUp(dt * -6.f);
	if (keyState.IsKeyDown(Keyboard::E))
		m_pCamera->MoveUp(dt * 6.f);

	if (mouseState.leftButton == true && m_MouseTracker.leftButton == m_MouseTracker.HELD)
	{
		float cubeTheta = (mouseState.x - lastMouseState.x);
		float cubePhi = (mouseState.y - lastMouseState.y);

		m_pCamera->Pitch(cubePhi * dt * 0.3f);
		m_pCamera->RotateY(cubeTheta * dt * 0.3f);
	}

	// 更新观察矩阵
	view = m_pCamera->GetViewXM();
}

void ExMainHUD::DrawScene()
{
	assert(m_pd3dImmediateContext);
	assert(m_pSwapChain);
	static float black[4] = { 0.0f, 0.0f, 0.0f, 1.0f };	// RGBA = (0,0,0,255)
	m_pd3dImmediateContext->ClearRenderTargetView(m_pRenderTargetView.Get(), black);
	m_pd3dImmediateContext->ClearDepthStencilView(m_pDepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

	D3DX11_TECHNIQUE_DESC techDesc;
	m_pTech->GetDesc(&techDesc);
	for (UINT p = 0; p < techDesc.Passes; ++p)
	{
		world = XMMatrixIdentity();

		m_pWorld->SetMatrix(reinterpret_cast<float*>(&world));
		m_pView->SetMatrix(reinterpret_cast<float*>(&view));
		m_pProj->SetMatrix(reinterpret_cast<float*>(&proj));

		m_pTech->GetPassByIndex(p)->Apply(0, m_pd3dImmediateContext.Get());
		// 绘制三角形
		m_pd3dImmediateContext->DrawIndexed(36, 0, 0);
	}
	//IDXGISwapChain::Present方法--前后台缓冲区交换并呈现
	HR(m_pSwapChain->Present(0, 0));
}

bool ExMainHUD::ExNativeEvent(const QByteArray& eventType, void* message, long* result)
{
	//return 0;

	MSG* msg = (MSG*)message;

	//qDebug() << "msg[" << msg->message << "]  WM_MOUSEMOVE[" << WM_MOUSEMOVE << "]  WM_ACTIVATEAPP[" << WM_ACTIVATEAPP << "]";

	switch (msg->message)
	{
	case WM_GETMINMAXINFO:
		((MINMAXINFO*)msg->lParam)->ptMinTrackSize.x = 200;
		((MINMAXINFO*)msg->lParam)->ptMinTrackSize.y = 200;
		return 0;
		// 监测这些键盘/鼠标事件
	case WM_INPUT:
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_XBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
	case WM_XBUTTONUP:
	case WM_MOUSEWHEEL:
	case WM_MOUSEHOVER:
	case WM_MOUSEMOVE:
		m_pMouse->ProcessMessage(msg->message, msg->wParam, msg->lParam);
		return false;

	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
	case WM_KEYUP:
	case WM_SYSKEYUP:
		m_pKeyboard->ProcessMessage(msg->message, msg->wParam, msg->lParam);
		return false;

	case WM_ACTIVATEAPP:
		m_pMouse->ProcessMessage(msg->message, msg->wParam, msg->lParam);
		m_pKeyboard->ProcessMessage(msg->message, msg->wParam, msg->lParam);
		return false;
	}

	return false;
}

void ExMainHUD::CalculateFrameStats()
{
	// 该代码计算每秒帧速，并计算每一帧渲染需要的时间，显示在窗口标题
	static int frameCnt = 0;
	static float timeElapsed = 0.0f;

	//保存这一秒内有多少帧
	frameCnt++;

	if ((m_Timer.TotalTime() - timeElapsed) >= 1.0f)
	{
		float fps = (float)frameCnt; // fps = frameCnt / 1
		float mspf = 1000.0f / fps;

		QString windowsTitle = QString().sprintf("MainHUD FPS[%d] Frame Time[%fms] FrameCnt[%d]", (int)fps, mspf, frameCnt);
		setWindowTitle(windowsTitle);

		// Reset for next average.
		frameCnt = 0;
		timeElapsed += 1.0f;
	}
}

HRESULT ExMainHUD::CreateShaderFromFile(const WCHAR* csoFileNameInOut, const WCHAR* hlslFileName, LPCSTR entryPoint, LPCSTR shaderModel, ID3DBlob** ppBlobOut)
{
	HRESULT hr = S_OK;

	// 寻找是否有已经编译好的顶点着色器
	if (csoFileNameInOut && exists(csoFileNameInOut))
	{
		return D3DReadFileToBlob(csoFileNameInOut, ppBlobOut);
	}
	else
	{
		DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG
		// 设置 D3DCOMPILE_DEBUG 标志用于获取着色器调试信息。该标志可以提升调试体验，
		// 但仍然允许着色器进行优化操作
		dwShaderFlags |= D3DCOMPILE_DEBUG;

		// 在Debug环境下禁用优化以避免出现一些不合理的情况
		dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
		ID3DBlob* errorBlob = nullptr;
		hr = D3DCompileFromFile(hlslFileName, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entryPoint, shaderModel,
			dwShaderFlags, 0, ppBlobOut, &errorBlob);
		if (FAILED(hr))
		{
			if (errorBlob != nullptr)
			{
				//OutputDebugStringA(reinterpret_cast<const char*>(errorBlob->GetBufferPointer()));
				qDebug() << reinterpret_cast<const char*>(errorBlob->GetBufferPointer());
			}
			SAFE_RELEASE(errorBlob);
			return hr;
		}

		// 若指定了输出文件名，则将着色器二进制信息输出
		if (csoFileNameInOut)
		{
			return D3DWriteBlobToFile(*ppBlobOut, csoFileNameInOut, FALSE);
		}
	}

	return hr;
}
